﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Text;
using System.Windows.Forms;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Text;
//MicroDesigner
using Jc.MicroDesigner.Management;
using Jc.MicroDesigner.Objects;

namespace Jc.MicroDesigner.Drawings
{
    /// <summary>
    /// Панель для визуализации выходных сигналов с элементов
    /// </summary>
    public partial class SignalsPanel : Panel
    {
        //Параметры рисования сигналов
        int _WidthPerSignal = 10; //Ширина отрисовки сигнала
        int _HeightPerSignal = 20; // Высота полосы сигнала
        int _SpaceHeight = 10; //Расстояние между полосами сигнала
        int _SpaceForTitles = 70;
        Font _DrawFont = null;

        int _TimeInterval = 10;
        bool _TimeTableVisible = false;
        bool _TimeTagsVisible = false;

        Pen pen = null;
        Color _PenColor = Color.Navy; //цвет отрисовки

        int SignalCount = 0;
        int StartSignal = 0;
        int StartData = 0;
        int DataCount = 0;

        //данные для перерисовки
        List<SignalContainer> _BackUp = null;

        public SignalsPanel()
            : this(null)
        {
        }

        public SignalsPanel(IContainer container)
        {
            if (container != null)
                container.Add(this);

            InitializeComponent();

            //Стили для производительной отрисовки содержимого
            SetStyle(ControlStyles.UserPaint | ControlStyles.AllPaintingInWmPaint | ControlStyles.OptimizedDoubleBuffer, true);
            pen = new Pen(_PenColor, 1.5f);
            this.BackColor = Color.White;
        }

        #region Methods 

        /// <summary>
        /// Сгенерировать диаграмму сигналов
        /// </summary>
        /// <param name="data">Данные</param>
        public void GenerateDiagram(List<SignalContainer> data)
        {
            if (data.Count > 0)
            {
                _BackUp = new List<SignalContainer>(data.ToArray());
                SignalContainer tmp = null;
                bool sorted = false;
                while (!sorted)
                {
                    sorted = true;
                    for (int i = 0; i < _BackUp.Count - 1; i++)
                    {
                        if (_BackUp[i].SortIndex > _BackUp[i + 1].SortIndex)
                        {
                            tmp = _BackUp[i];
                            _BackUp[i] = _BackUp[i + 1];
                            _BackUp[i + 1] = tmp;
                            sorted = false;
                        }
                    }
                }
                ViewRange(0, 0);
            }
        }

        /// <summary>
        /// Сгенерировать диаграмму
        /// </summary>
        /// <param name="data">Источники</param>
        /// <param name="start">Начальное время</param>
        /// <param name="length">Продолжительность</param>
        /// <param name="beginsignal">Начальный источник</param>
        /// <param name="datacount">Количество источников</param>
        /// <returns>Изображение</returns>
        private Image GenImage(List<SignalContainer> data, int start, int length, int beginsignal, int datacount)
        {
            Image img = null;
            if ((data != null) && (data.Count > 0) && (start >= 0) && (length > 0))
            {
                //Выделяем большую область для построения
                img = new Bitmap(length * _WidthPerSignal,
                    (data.Count + 1) * (_SpaceHeight + _HeightPerSignal) + _SpaceHeight);
                Graphics g = Graphics.FromImage(img);
                PaintImg(g, data, start, length, beginsignal, datacount);
            }
            return img;
        }

        /// <summary>
        /// Отрисовка изображения в объект Graphics
        /// </summary>
        /// <param name="g">Graphics</param>
        /// <param name="data">Данные</param>
        /// <param name="start">Начальная отметка сигнала</param>
        /// <param name="length">Количество сигналов</param>
        /// <param name="beginsignal">Начальная отметка данных</param>
        /// <param name="datacount">Количество строк данных</param>
        public void PaintImg(Graphics g, List<SignalContainer> data, int start, int length, int beginsignal, int datacount)
        {
            if ((data != null) && (data.Count > 0) && (start >= 0) && (length > 0))
            {
                if (_DrawFont == null)
                    _DrawFont = Font;
                //Выделяем большую область для построения
                g.TextRenderingHint = TextRenderingHint.SingleBitPerPixel;

                int dtime = _TimeInterval * _WidthPerSignal;
                //количество меток
                int line_count = Math.Min(data[0].Capacity - start, length) / _TimeInterval + 1;

                //отрисовка сетки
                if (_TimeTableVisible)
                {
                    Pen dashed_pen = new Pen(Color.Silver, 1.5f);
                    dashed_pen.DashStyle = DashStyle.Dash;
                    for (int i = 0; i <= line_count; i++)
                    {
                        int n = Math.Min(beginsignal + datacount, data.Count) - beginsignal;
                        g.DrawLine(dashed_pen, i * dtime + _SpaceForTitles,
                            0, i * dtime + _SpaceForTitles, (_SpaceHeight + _HeightPerSignal) * n + _HeightPerSignal);
                    }
                }
                Pen p = new Pen(Color.Black, 1.5f);
                //Рисуем график
                int x = _SpaceForTitles;
                int y = _SpaceHeight;
                for (int si = beginsignal; (si < beginsignal + datacount) && (si < data.Count); si++)
                {
                    SignalContainer sigc = data[si];
                    //рисуем метку элемента, вынесено на отдельный компонент

                    TextRenderer.DrawText(g, sigc.Label, _DrawFont, new Rectangle(0, y, _SpaceForTitles, _HeightPerSignal),
                        p.Color, Color.White, TextFormatFlags.Right);

                    sbyte first = sigc[start];
                    int ly = 0;
                    if (first < 0)
                        ly = _HeightPerSignal / 2;
                    else
                        ly = (1 - first) * _HeightPerSignal;
                    #region Отрисовка одной полосы

                    for (int i = start; (i < sigc.Capacity) && (i < start + length); i++)
                    {
                        if (sigc[i] == first)
                        {
                            g.DrawLine(pen, x, y + ly, x + _WidthPerSignal, y + ly);
                        }
                        else
                        {
                            int ny = 0;
                            if (sigc[i] >= 0)
                                ny = (1 - sigc[i]) * _HeightPerSignal;
                            else
                                ny = _HeightPerSignal / 2;
                            g.DrawLine(pen, x, y + ly, x, y + ny);
                            ly = ny;
                            g.DrawLine(pen, x, y + ly, x + _WidthPerSignal, y + ly);
                        }
                        first = sigc[i];
                        x += _WidthPerSignal;
                    }

                    #endregion
                    y += _SpaceHeight + _HeightPerSignal;
                    x = _SpaceForTitles;
                }

                //отрисовка меток
                if (_TimeTagsVisible)
                {
                    for (int i = 0; i <= line_count; i++)
                    {
                        string time = (start + i * _TimeInterval).ToString();
                        int fx = i * dtime + _SpaceForTitles;
                        int fy = y;
                        g.DrawLine(p, fx, y, fx, y + _HeightPerSignal);
                        //g.DrawString(time, _DrawFont, p.Brush, new PointF(fx, fy));
                        TextRenderer.DrawText(g, time, _DrawFont, new Point(fx, fy), p.Color, Color.White);
                    }
                }
            }
        }

        /// <summary>
        /// Отрисовать диапазон
        /// </summary>
        /// <param name="start">Начало</param>
        /// <param name="begin_signal">Длина</param>
        public void ViewRange(int start, int begin_signal)
        {
            SignalCount = Width / _WidthPerSignal;
            DataCount = (Height - _SpaceHeight - _HeightPerSignal) / _HeightPerSignal;
            StartSignal = start;
            StartData = begin_signal;
            Invalidate();
        }

        /// <summary>
        /// Сохранить некоторую область диаграммы в файл
        /// </summary>
        /// <param name="start">Начальное время</param>
        /// <param name="length">Длительность</param>
        /// <param name="filename">Файл изображения</param>
        public void SaveToImage(string filename)
        {
            //Image img = GenImage(_BackUp, StartSignal, SignalCount, 0, _BackUp.Count);
            //img.Save(filename);
            Bitmap p = new Bitmap(this.Width, this.Height);
            this.DrawToBitmap(p, new Rectangle(0, 0, Width, Height));
            p.Save(filename);
        }

        /// <summary>
        /// Отрисовка сигналов
        /// </summary>
        /// <param name="e"></param>
        protected override void OnPaint(PaintEventArgs e)
        {
            PaintImg(e.Graphics, _BackUp, StartSignal, SignalCount, StartData, DataCount);
        }

        #endregion

        #region Properties

        /// <summary>
        /// Ширина полосы единичного сигнала
        /// </summary>
        [Category("Drawings")]
        [Description("Ширина полосы единичного сигнала")]
        [DefaultValue(5)]
        public int SignalWidth
        {
            get
            {
                return _WidthPerSignal;
            }
            set
            {
                _WidthPerSignal = value;
            }
        }

        /// <summary>
        /// Высота полосы единичного сигнала
        /// </summary>
        [Category("Drawings")]
        [Description("Высота полосы единичного сигнала")]
        [DefaultValue(20)]
        public int SignalHeight
        {
            get
            {
                return _HeightPerSignal;
            }
            set
            {
                _HeightPerSignal = value;
            }
        }

        /// <summary>
        /// Расстояние между полосами сигналов
        /// </summary>
        [Category("Drawings")]
        [Description("Расстояние между полосами сигналов")]
        [DefaultValue(10)]
        public int SignalVerticalSpace
        {
            get
            {
                return _SpaceHeight;
            }
            set
            {
                _SpaceHeight = value;
            }
        }

        /// <summary>
        /// Цвет отрисовки сигнала
        /// </summary>
        [Category("Drawings")]
        [Description("Цвет отрисовки сигнала")]
        public Color PenColor
        {
            get
            {
                return _PenColor;
            }
            set
            {
                _PenColor = value;
            }
        }

        /// <summary>
        /// Интервал времени для отметок
        /// </summary>
        [Category("Drawings")]
        [Description("Интервал времени для отметок")]
        [DefaultValue(10)]
        public int TimeInterval
        {
            get
            {
                return _TimeInterval;
            }
            set
            {
                _TimeInterval = value;
            }
        }

        /// <summary>
        /// Показывать ли временные отметки
        /// </summary>
        [Category("Drawings")]
        [Description("Показывать ли временные отметки")]
        [DefaultValue(false)]
        public bool TimeTagsVisible
        {
            get
            {
                return _TimeTagsVisible;
            }
            set
            {
                _TimeTagsVisible = value;
            }
        }

        /// <summary>
        /// Показывать ли сетку времени
        /// </summary>
        [Category("Drawings")]
        [Description("Показывать ли сетку времени")]
        [DefaultValue(false)]
        public bool TimeTableVisible
        {
            get
            {
                return _TimeTableVisible;
            }
            set
            {
                _TimeTableVisible = value;
            }
        }

        /// <summary>
        /// Шрифт для надписей на диаграмме
        /// </summary>
        [Category("Drawings")]
        [Description("Шрифт для надписей на диаграмме")]
        public Font DiagramFont
        {
            get
            {
                return _DrawFont;
            }
            set
            {
                _DrawFont = value;
            }
        }

        #endregion
    }
}